// This is the main DLL file.

#include "stdafx.h"

#include <stdio.h>
#include <stdlib.h>

#include "Hopfield.MathCore.h"

namespace Hopfield {

	public ref class HopfieldMathCore {
		float msemin;
		TPPM* PPM;
		int col;
		int row;
		int Zoom;
		char* fcname;
		THopNet* HopNet;

	private:
		char HopfieldMathCore::getchar(int c){
			switch(c){
				case 0: return '0';
				case 1: return '1';
				case 2: return '2';
				case 3: return '3';
				case 4: return '4';
				case 5: return '5';
				case 6: return '6';
				case 7: return '7';
				case 8: return '8';
				case 9: return '9';
			}
			return '\0';
		}

		void HopfieldMathCore::makefcname(int c){
			for(int i = 9, d = 10; i > 0; i--, d *= 10){
				fcname[i] = getchar((c%d - c%(d/10))/(d/10));
			}
		}

	public:
		HopfieldMathCore::~HopfieldMathCore() {
			delete HopNet;
			delete PPM;
		}

		HopfieldMathCore::HopfieldMathCore() {
			msemin = 0.001f;
			PPM = new TPPM();
			col = 0;
			row = 0;
			Zoom = 1;
			fcname = "0000000000.ppm";
			HopNet = new THopNet();
		}

		void HopfieldMathCore::ReCreate(int n) {
			if(HopNet->ReCreate(n))
				printf("Create new network with %i neurons failture.\n",n);
			else
				printf("Create new network with %i neurons success.\n",n);
		}

		void HopfieldMathCore::SetMSEmin(float nmsemin) {
			msemin = nmsemin;
			printf("Set MSEmin = %1.10f success.\n",msemin);
		}

		void HopfieldMathCore::OpenHNFile(char* fname) {
			FILE* open_from = NULL;
			fopen_s(&open_from, fname, "r");
			if (open_from != NULL) {
				int colorized = 0;
				fscanf_s(open_from,"C(%i)\n",&colorized);
				PPM->Colorized = colorized == 1 ? true : false;
				fscanf_s(open_from,"X(%i)\n",&col);
				fscanf_s(open_from,"Y(%i)\n",&row);
				int iarg;
				fscanf_s(open_from,"DL(%i)\n",&iarg);
				HopNet->Setd(iarg);
				float farg;
				fscanf_s(open_from,"E(%f)\n",&farg);
				HopNet->Seteta(farg);
				if(HopNet->ScanAllMatrix(open_from))
					printf("Open network from \"%s\" file failture.\n",fname);
				else
					printf("Open network from \"%s\" file success.\n",fname);
				fclose(open_from);
			}
		}

		void HopfieldMathCore::SaveHNFile(char* fname) {
			FILE* save_to = NULL;
			fopen_s(&save_to, fname, "w");
			if(save_to != NULL) {
				fprintf(save_to,"C(%i)\n",PPM->Colorized ? 1 : 0);
				fprintf(save_to,"X(%i)\n",col);
				fprintf(save_to,"Y(%i)\n",row);
				fprintf(save_to,"DL(%i)\n",HopNet->Getd());
				fprintf(save_to,"E(%f)\n",HopNet->Geteta());
				if(HopNet->PrintAllMatrix(save_to))
					printf("Save network to \"%s\" file failture.\n",fname);
				else
					printf("Save network to \"%s\" file success.\n",fname);
				fclose(save_to);
			}
		}

		void HopfieldMathCore::SaveImgFile(char* fname) {
			int zoom = PPM->Zoom;
			PPM->Zoom = Zoom;
			if(PPM->SaveAsPPM(fname, HopNet->GetY(), row, col))
				printf("Save Y matrix to \"%s\" file failture.\n",fname);
			else
				printf("Save Y matrix to \"%s\" file success.\n",fname);
			PPM->Zoom = zoom;
		}

		void HopfieldMathCore::OpenImgFile(char* fname, bool colorized) {
			if(PPM->Colorized != colorized || ((col == 0) && (row == 0)) || HopNet->Getl() == 0){
				//PPM->GetColRowPPM(fname, &col, &row);
				col = PPM->GetColPPM(fname);
				row = PPM->GetRowPPM(fname);
				if(col == 0 && row == 0) {
					printf("Load Y matrix from \"%s\" file failture.\n",fname);
					return;
				}
				PPM->Colorized = colorized;
				if (true == colorized)
					ReCreate(col*row*3); // 3 - R G B
				else
					ReCreate(col*row);
			}
			if(PPM->LoadFromPPM(fname, HopNet->GetY(), row, col))
				printf("Load Y matrix from \"%s\" file failture.\n",fname);
			else
				printf("Load Y matrix from \"%s\" file success.\n",fname);
		}

		void HopfieldMathCore::LoadKtoY(int k){
			if((HopNet->Getl() < k)) {
				printf("This net has only %i attractors.\n",HopNet->Getl());
				printf("Load %i attractor from L to Y matrix failture.\n",k);
				return;
			}
			if(k < 0) {
				printf("You try to load attractor \"%i\" that no exist.\n",k);
				printf("Load %i attractor from L to Y matrix failture.\n",k);
				return;
			}
			if(HopNet->LtoY(k))
				printf("Load %i attractor from L to Y matrix failture.\n",k);
			else
				printf("Load %i attractor from L to Y matrix success.\n",k);
		}

		void HopfieldMathCore::PrintHN(void) {
			if(HopNet->PrintAllMatrix(stdout))
				printf("Print network failture.\n");
			else
				printf("Print network success.\n");
		}

		void HopfieldMathCore::RandY(int p) {
			HopNet->RandY(p);
		}


		void HopfieldMathCore::AddAttr() {
			if(HopNet->AddL())
				printf("Add attractor to L matrix failture.\n");
			else
				printf("Add attractor to L matrix success.\n");
		}

		void HopfieldMathCore::DelAttr(int k) {
			if((HopNet->Getl() < k)) {
				printf("This net has only %i attractors.\n",HopNet->Getl());
				printf("Del %i attractor from network failture.\n",k);
				return;
			}
			if(k < 0) {
				printf("You try to delele attractor \"%i\" that no exist.\n",k);
				printf("Del %i attractor from network failture.\n",k);
				return;
			}
			if(HopNet->DelL(k))
				printf("Del %i attractor from network failture.\n",k);
			else
				printf("Del %i attractor from network success.\n",k);
		}

		void HopfieldMathCore::Setd(int newd) {
			int rd = HopNet->Setd(newd);
			if(rd != newd)
				printf("Set d = %i parameter failture.\n",newd);
			else
				printf("Set d = %i parameter success.\n",newd);
		}

		void HopfieldMathCore::Seteta(float neweta) {
			float reta = HopNet->Seteta(neweta);
			if(reta != neweta)
				printf("Set eta = %f paraeter failture.\n",neweta);
			else
				printf("Set eta = %f paraeter success.\n",neweta);
		}

		void HopfieldMathCore::LearnHN(char* arg) {
			switch(*arg) {
				case 'h':
					if(HopNet->LearnH())
						printf("Learn network using Hebbian rule algorithm "
						"failture.\n");
					else
						printf("Learn network using Hebbian rule algorithm "
						"success.\n");
					break;
				case 'd':
					if(HopNet->LearnDMSE(msemin))
						printf("Learn network using delta rule "
						"algorithm failture.\n");
					else
						printf("Learn network using delta rule "
						"algorithm success.\n");
					break;
				case 'z':
					if(HopNet->LearnMPRMSE(msemin))
						printf("Learn network using modify perceptron rule "
						"failture.\n");
					else
						printf("Learn network using modify perceptron rule "
						"success.\n");
					break;
				case 'p':
					if(HopNet->LearnPMSE(msemin))
						printf("Learn network using delta rule "
						"failture.\n");
					else
						printf("Learn network using delta rule "
						"success.\n");
					break;
				case 's':
					if(HopNet->LearnSMSE(msemin))
						printf("Learn network using delta rule for predictor"
						"failture.\n");
					else
						printf("Learn network using delta rule for predictor"
						"success.\n");
					break;
			}
		}

		float LearnStepHN(char* arg) {
			float mse = 0.0;
			switch(*arg) {
				case 'h':
					if(HopNet->LearnH())
						printf("Learn network using Hebbian rule algorithm "
						"failture.\n");
					else
						printf("Learn network using Hebbian rule algorithm "
						"success.\n");
					break;
				case 'd':
					mse = HopNet->LearnD();
					printf("Learn network using delta rule "
					"algorithm success.\n");
					break;
				case 'z':
					mse = HopNet->LearnMPR();
					printf("Learn network using modify perceptron rule "
					"success.\n");
					break;
				case 'p':
					mse = HopNet->LearnP();
					printf("Learn network using delta rule for predictor"
					"success.\n");
					break;
				case 's':
					mse = HopNet->LearnS();
					printf("Learn network using delta rule for sequence"
					"success.\n");
					break;
			}
			return mse;
		}

		float HopfieldMathCore::CompHN(char* arg) {
			float mse = 0.0;
			switch(*arg) {
				case 'a':
					printf("Compute net state asynchronously.\n");
					mse = HopNet->MSECompA();
					break;
				case 's':
					printf("Compute net state synchronously.\n");
					mse = HopNet->MSECompS();
					break;
				case 'r':
					printf("Compute net state randomly.\n");
					mse = HopNet->MSECompR();
					break;
			}
			return mse;
		}

		void HopfieldMathCore::ClearW()
		{
			HopNet->ClearW();
		}

		void HopfieldMathCore::SetY(float* nY)
		{
            float* pY = HopNet->GetY();
            for (int i = 0; i < HopNet->Getn(); i++)
                pY[i] = nY[i];
		}

		float* HopfieldMathCore::GetY() {
			return HopNet->GetY();
		}

		int HopfieldMathCore::Getn() {
			return HopNet->Getn();
		}

		float** HopfieldMathCore::GetW() {
			return HopNet->GetW();
		}

		int HopfieldMathCore::Getw() {
			return HopNet->Getw();
		}

		float** HopfieldMathCore::GetL() {
			return HopNet->GetL();
		}

		int HopfieldMathCore::GetRecognizedLIndex(int count, float &mseRec) {
			if (count > HopNet->Getn() || count <= 0)
				count = HopNet->Getn();
			float mseMin = (float)HopNet->Getn();
			float *Y = HopNet->GetY();
			float **L = HopNet->GetL();
			int recognizedLindex = -1;
			for(int i = 0; i < HopNet->Getl(); i++)
			{
				float mse = 0.0;
				#pragma omp parallel for reduction(+: mse) shared(Y, L)
				for(int j = 0; j < count; j++) {
					mse += (Y[j] - L[i][j])*(Y[j] - L[i][j]);
				}

				mse = sqrt(mse/(float)count);
				if (mse < mseMin) {
					recognizedLindex = i;
					mseMin = mse;
				}
			}
			mseRec = mseMin;
			return recognizedLindex;
		}

		int HopfieldMathCore::Getl() {
			return HopNet->Getl();
		}

		int HopfieldMathCore::GetCols()
		{
			return col;
		}

		int HopfieldMathCore::GetRows()
		{
			return row;
		}

		float HopfieldMathCore::GetE()
		{
			return HopNet->E();
		}

		int HopfieldMathCore::Getd() {
			return HopNet->Getd();
		}

	};
}
